iT邦幫忙

2022 iThome 鐵人賽

DAY 8
1

Medium 清新閱讀版連結

前一天我們演練了 API 相關的基本測試方式,今天我們來看一下資料庫的測試方式吧!

前置: Factory & UserRepository

在開始實作資料庫測試之前,先與大家介紹 Factory 這個東西。

Factory 是個 Laravel 的 ORM:Eloquent 提供的功能,它可以讓我們用很簡單的方式,去準備測試資料,在 Laravel 初始化後,預設已經幫我們準備好了一個 UserFactory (而之所以 Laravel 官方預先準備了這個 Factory,可能是因為最基本預設的資料表,就是 users),慣例上會將 Factory 檔放在 database/factories 這個資料夾下,並且會命名為 XxxFactory ,其中 Xxx 是資源名稱:

  • database/factories/UserFactory.php
<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
 */
class UserFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition()
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
            'remember_token' => Str::random(10),
        ];
    }

    /**
     * Indicate that the model's email address should be unverified.
     *
     * @return static
     */
    public function unverified()
    {
        return $this->state(fn (array $attributes) => [
            'email_verified_at' => null,
        ]);
    }
}

Factory 有2種主要的使用方式,第1種是會在資料表實際建立資料,並且回應 Eloquent Enity,用法如下:

$user = User::facotry()->create();

以上程式碼會立即在資料庫建立 User資料,其中 nameemail 會使用 fake() 來隨機建立假值,email_verified_at 會用當下時間,remember_token 則會是一個隨機產生的 10 個字的字串。

另1種方式,則不會在資料庫建立資料,但仍會回應 Eloquent Enity,用法如下:

$user = User::facotry()->make();

不過要注意,用 make() 來建立資料實體,其 id 欄位值會是空值喔(因為沒有實際在資料庫建立資料)。

此外,也可以在建立資料時,指定你想要的欄位值:

$user = User::facotry()->create(['name' => 'william']);

以上就是 Factory 的相關介紹,當然其實還有更多有趣的技術細節值得探討,像是神秘的 fake() ,不過這讓我們在之後的文章再來探討吧!

理解了準備資料的方式後,讓我們來看看今天要測試的對象,以下是針對 User 這個資源,所實作的資料操作類別 UserRepository

  • app/Repositories/UserRepository.php
<?php

namespace App\Repositories;

use App\Models\User;

class UserRepository
{
    protected $model;

    public function __construct(User $model)
    {
        $this->model = $model;
    }

    // 依給予的輸入資料,於資料庫中建立 User 資料
    public function createUser(array $data)
    {
        return $this->model::create($data);
    }

    // 更新指定 ID 之 User 資料之姓名值
    public function updateUserNameById($userId, string $name)
    {
        $user = $this->getUserById($userId);

        if (empty($user)) {
            return false;
        }

        $user->name = $name;

        return $user->save();
    }

    // 刪除指定 ID 之 User 資料
    public function deleteUserById($userId)
    {
        $user = $this->getUserById($userId);

        if (empty($user)) {
            return false;
        }

        return $user->delete();
    }

    // 讀取指定 ID 之 User 資料
    public function getUserById($userId)
    {
        return $this->model::find($userId);
    }
}

以上程式碼實作了關於 User 這個資源的 CRUD 4種操作,接著就來寫測試吧!

資料操作測試:建立

首先是建立資料功能的測試,請看以下程式碼:

namespace Tests\Feature;

use App\Repositories\UserRepository;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class UserRepositoryTest extends TestCase
{
    use RefreshDatabase;

    /**
     * Example for user creating
     * @return void
     */
    public function testUserCreating()
    {
        $repository = app(UserRepository::class);

        $repository->createUser([
            'name' => 'test_name',
            'email' => 'test@test.test',
            'password' => 'password',
        ]);

        $this->assertDatabaseHas('users', [
            'name' => 'test_name',
            'email' => 'test@test.test',
            'password' => 'password',
        ]);
    }
}

以上程式碼,會呼叫 createUser() ,並在呼叫之後,驗證資料庫中是否存有期望中的資料。

資料操作測試:讀取

namespace Tests\Feature;

use App\Repositories\UserRepository;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class UserRepositoryTest extends TestCase
{
    use RefreshDatabase;

		/**
     * Example for user reading
     * @return void
     */
    public function testUserReading()
    {
        $user = User::factory()->create();
        $repository = app(UserRepository::class);

        $userGot = $repository->getUserById($user->id);

        $this->assertEquals($user->id, $userGot->id);
    }
}

以上程式碼,會先建立假資料 $user ,之後呼叫 getUserById() ,並在呼叫之後,驗證取得的資料之 ID 是否如預期。

資料操作測試:更新

namespace Tests\Feature;

use App\Repositories\UserRepository;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class UserRepositoryTest extends TestCase
{
    use RefreshDatabase;

		/**
     * Example for user updating
     * @return void
     */
    public function testUserUpdating()
    {
        $user = User::factory()->create([
            'name' => 'test_name',
            'email' => 'test@test.test',
            'password' => 'password',
        ]);
        $repository = app(UserRepository::class);

        $repository->updateUserNameById($user->id, 'name_2');

        $this->assertDatabaseHas('users', [
            'name' => 'name_2',
        ]);
    }
}

以上程式碼,會先建立假資料 $user ,之後呼叫 updateUserNameById() ,並在呼叫之後,驗證資料是否如期更新。

資料操作測試:刪除

namespace Tests\Feature;

use App\Repositories\UserRepository;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class UserRepositoryTest extends TestCase
{
    use RefreshDatabase;

		/**
     * Example for user deletion
     * @return void
     */
    public function testUserDeletion()
    {
        $user = User::factory()->create([
            'name' => 'test_name',
            'email' => 'test@test.test',
            'password' => 'password',
        ]);
        $userId = $user->id;
        $repository = app(UserRepository::class);

        $repository->deleteUserById($user->id);

        $this->assertDatabaseMissing('users', [
            'id' => $userId,
        ]);
    }
}

以上程式碼,會先建立假資料 $user ,之後呼叫 deleteUseryId() ,並在呼叫之後,驗證資料是否如預期地刪除。

以上就是今天的資料庫測試介紹。

明天來為大家介紹與自動化測試相關的 Traits。

參考資料


上一篇
API測試
下一篇
自動化測試相關Traits介紹
系列文
自動化測試大作戰31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言